package cn.lyjuan.payment.third.http.api.ctl;

import cn.lyjuan.base.exception.impl.BaseCode;
import cn.lyjuan.base.exception.util.ErrUtils;
import cn.lyjuan.base.http.ctl.BaseCtl;
import cn.lyjuan.base.http.vo.res.BaseRes;
import cn.lyjuan.base.util.SpringUtils;
import cn.lyjuan.base.util.URLUtils;
import cn.lyjuan.payment.third.base.common.enums.impl.ChlEnum;
import cn.lyjuan.payment.third.base.common.enums.impl.PayStatusEnum;
import cn.lyjuan.payment.third.base.model.app.domain.AppModel;
import cn.lyjuan.payment.third.base.model.app.service.AppService;
import cn.lyjuan.payment.third.base.model.chl.domain.AppChlModel;
import cn.lyjuan.payment.third.base.model.chl.domain.IChlCnfModel;
import cn.lyjuan.payment.third.base.model.chl.service.AppChlService;
import cn.lyjuan.payment.third.base.model.exception.ErrCodeImpl;
import cn.lyjuan.payment.third.base.model.pay.domain.PayModel;
import cn.lyjuan.payment.third.base.model.pay.service.PayService;
import cn.lyjuan.payment.third.base.model.pay.vo.NotifyResVo;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.bean.AliChlCnf;
import cn.lyjuan.payment.third.base.pay.chl.impl.ali.util.AlipaySubmit;
import cn.lyjuan.payment.third.base.pay.chl.impl.iap.bean.IosNotifyRes;
import cn.lyjuan.payment.third.http.api.exception.ApiCode;
import cn.lyjuan.payment.third.http.api.mq.enums.MqTypeEnum;
import cn.lyjuan.payment.third.http.api.mq.msg.OrderPayMsg;
import cn.lyjuan.payment.third.http.api.vo.req.*;
import cn.lyjuan.payment.third.http.api.vo.res.AuthResVo;
import cn.lyjuan.payment.third.http.api.vo.res.PayAppConfigResVo;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.hehewang.hhw.tdmq.client.producer.AMQProducer;
import com.hehewang.hhw.tdmq.message.BaseMsg;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Api(tags = "支付")
@Slf4j
@RestController
@RequestMapping("pay")
public class PayCtl extends BaseCtl {
    @Autowired
    private AppService appService;

    @Autowired
    private AppChlService appChlService;

    @Autowired
    private PayService payService;

    @Resource(name = "orderMq")
    private AMQProducer amqProducer;

    @ApiOperation("转账")
    @PostMapping(value = "transfer")
    public BaseRes transfer(@Valid TransferReqVo order) {
        AppModel appModel = appService.query(order.getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // check iap
        if (order.getChlType() == null || order.getChlType().equals(ChlEnum.IAP)) {
            ErrUtils.appThrow(ApiCode.NOT_SUPPORT_IAP_REFUND);
        }
        // 验证签名
        checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(order.getAppId(), order.getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 获取交易订单信息
        Object res = payService.transfer(appModel, appChlModel, order.getAppNo(), order.getTitle(), order.getIdentity(),
                order.getParams().getRealName(), order.getRemark(), order.getFlag(), order.getAmt(), order.getParams().params(order.getChlType()));
        return BaseRes.resp(res);
    }

    @ApiOperation("获取转账费率")
    @PostMapping(value = "transfer/rate")
    public PayAppConfigResVo transferRate(@Valid PayAppConfigReqVo payAppConfigReqVo) {
        // find reg app
        AppModel appModel = appService.query(payAppConfigReqVo.getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // check chl
        if (payAppConfigReqVo.getChlType() == null) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        // app pay chl
        AppChlModel appChlModel = appChlService.query(payAppConfigReqVo.getAppId(), payAppConfigReqVo.getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 组装配置数据
        PayAppConfigResVo res = new PayAppConfigResVo();
        Double rate = appChlModel.getEntity().getTranFee();
        res.setTransferRate(rate != null ? rate : 0.0);
        return res;
    }

    @ApiOperation("发起订单支付")
    @PostMapping(value = "order")
    public BaseRes order(@Valid OrderReq order) {
        AppModel appModel = appService.query(order.getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(order.getAppId(), order.getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 获取交易订单信息
        Object res = payService.order(appModel, appChlModel, order.getAppNo(), order.getGoods(), order.getFlag(),
                order.getAmt(), order.getNotifyUrl(), order.getReturnUrl(), order.getParams().params(order.getChlType()));
        return BaseRes.resp(res);
    }

    @ApiOperation("交易状态异步通知")
    @RequestMapping(value = "notify/{localNo}")
    public Object notifyS(@PathVariable String localNo) {
        NotifyResVo vo = payService.notifyStatus(localNo);
        PayModel payModel = payService.query(localNo);
        if (null == payModel)
            ErrUtils.appThrow(ErrCodeImpl.PAY_APP_TRAN_NO_NOT_FOUND);
        OrderPayMsg orderPayMsg = new OrderPayMsg();
        orderPayMsg.setOrderNo(payModel.getEntity().getAppNo());
        orderPayMsg.setLocalNo(localNo);
        orderPayMsg.setPayStatus(vo.getStatus().getCode());
        orderPayMsg.setCreateTime(LocalDateTime.now());
        amqProducer.sendMsg(BaseMsg.msg(MqTypeEnum.ORDER_PAY, orderPayMsg));
        return vo.getRespMsg();
    }

    @ApiOperation("苹果内购状态异步通知")
    @RequestMapping(value = "/ios/notify")
    public ResponseEntity<String> iosNotify(@Valid IosNotifyRes iosNotifyRes) {
        ResponseEntity<String> res = new ResponseEntity<>(null, HttpStatus.NO_CONTENT);
        // check NotifyParam
        if (iosNotifyRes == null ||
                iosNotifyRes.getUnified_receipt() == null ||
                iosNotifyRes.getPassword() == null ||
                iosNotifyRes.getUnified_receipt().getLatest_receipt_info().size() < 1 ||
                iosNotifyRes.getUnified_receipt().getStatus() != 0
        ) {
            return res;
        }
        payService.handleIosNotify(iosNotifyRes.getPassword(), iosNotifyRes.getNotification_type(), iosNotifyRes.getUnified_receipt());
        return res;
    }

    @ApiOperation("验证IAP交易状态")
    @PostMapping(value = "ios/verify")
    public BaseRes<PayStatusEnum> iosVerify(
            @RequestBody @Valid IapVerifyReqVo iapVerifyReqVo
    ) {
        PayModel payModel = payService.query(iapVerifyReqVo.getLocalNo());
        if (null == payModel) {
            ErrUtils.appThrow(ApiCode.PAY_TRAN_NOT_FOUND);
        }
        AppModel appModel = appService.query(payModel.getEntity().getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        //checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(payModel.getEntity().getAppId(), payModel.getEntity().getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        Map<String, String> map = new HashMap<>(3);
        String receipt = URLUtils.code(iapVerifyReqVo.getReceipt(), StandardCharsets.UTF_8.name(), false);
        // todo java jdk  URLEncoder.encode 与 open jdk 不同
        receipt = receipt.replaceAll("%2B", "+");
        map.put("receipt", receipt);
        if (log.isDebugEnabled())
            log.debug("iap receipt decode ==> {}", map.get("receipt"));
        map.put("productId", iapVerifyReqVo.getProductId());
        map.put("transactionId", iapVerifyReqVo.getTransactionId());
        map.put("devType", iapVerifyReqVo.getDevType().getValue());
        PayStatusEnum status = payService.queryStatus(iapVerifyReqVo.getLocalNo(), appChlModel, map);
        if (status.equals(PayStatusEnum.SUCC)) {
            OrderPayMsg orderPayMsg = new OrderPayMsg();
            orderPayMsg.setOrderNo(payModel.getEntity().getAppNo());
            orderPayMsg.setLocalNo(payModel.getEntity().getLocalNo());
            orderPayMsg.setPayStatus(1);
            orderPayMsg.setCreateTime(LocalDateTime.now());
            amqProducer.sendMsg(BaseMsg.msg(MqTypeEnum.ORDER_PAY, orderPayMsg));
        }
        return BaseRes.resp(status);
    }

    // todo: 等待提取成 渠道分发
    @ApiOperation("授权")
    @PostMapping(value = "auth")
    public BaseRes<AuthResVo> auth(@Valid AuthReqVo authReqVo) {
        if (!authReqVo.getChlType().equals(ChlEnum.ALI)) {
            ErrUtils.appThrow(BaseCode.FAILED, "auth error: only support alipay auth");
        }
        AppModel appModel = appService.query(authReqVo.getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(authReqVo.getAppId(), authReqVo.getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 配置
        IChlCnfModel chlCnfModel = appChlModel.dispatch();
        // ali handle auth
        AliChlCnf aliChlCnf = (AliChlCnf) chlCnfModel.pkg();
        DefaultAlipayClient certAlipayClient = AlipaySubmit.makeCertAlipayClient(aliChlCnf.getCertPath(), aliChlCnf, false);
        AlipaySystemOauthTokenResponse authData = AlipaySubmit.authCodeExchangeAccessToken(certAlipayClient, authReqVo.getAuthCode());
        if (authData == null || !authData.isSuccess()) {
            ErrUtils.appThrow(BaseCode.FAILED);
        }
        AlipayUserInfoShareResponse aliUserData = AlipaySubmit.accessTokenExchangeUserShareInfo(certAlipayClient, authData.getAccessToken());
        if (aliUserData == null || !aliUserData.isSuccess()) {
            ErrUtils.appThrow(BaseCode.FAILED);
        }
        // make response
        AuthResVo res = new AuthResVo();
        res.setAuthData(authData);
        res.setAliUserData(aliUserData);
        return BaseRes.resp(res);
    }

    // todo: 等待提取到 渠道分发
    @ApiOperation("签名请求串")
    @PostMapping(value = "sign")
    public BaseRes<String> signStr(@Valid SignReqVo signReqVo) {
        if (!signReqVo.getChlType().equals(ChlEnum.ALI)) {
            ErrUtils.appThrow(BaseCode.FAILED, "auth error: only support alipay auth");
        }
        AppModel appModel = appService.query(signReqVo.getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(signReqVo.getAppId(), signReqVo.getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // 配置
        IChlCnfModel chlCnfModel = appChlModel.dispatch();
        // ali handle auth
        AliChlCnf aliChlCnf = (AliChlCnf) chlCnfModel.pkg();
        Map<String, String> params = new HashMap<>();
        params.put("app_id", aliChlCnf.getAppId());
        params.put("pid", aliChlCnf.getMchId());
        // get params
        SignReqVo.SignParams params1 = signReqVo.getParams();
        params.put("apiname", params1.getApiName());
        params.put("method", params1.getMethod());
        params.put("app_name", params1.getAppName());
        params.put("biz_type", params1.getBizType());
        params.put("product_id", params1.getProductId());
        params.put("scope", params1.getScope());
        params.put("target_id", params1.getTargetId());
        params.put("auth_type", params1.getAuthType());
        params.put("sign_type", params1.getSignType());
        String signStr = AlipaySubmit.signForPayCtl(params, aliChlCnf.getRsaPri());
        return BaseRes.resp(signStr);
    }

    @ApiOperation("查询交易状态")
    @GetMapping(value = "query")
    public BaseRes<PayStatusEnum> query(
            @ApiParam("应用方的交易流水号") @RequestParam @NotNull String localNo) {
        PayModel payModel = payService.query(localNo);
        if (null == payModel) {
            ErrUtils.appThrow(ApiCode.PAY_TRAN_NOT_FOUND);
        }
        AppModel appModel = appService.query(payModel.getEntity().getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        AppChlModel appChlModel = appChlService.query(payModel.getEntity().getAppId(), payModel.getEntity().getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        PayStatusEnum status = payService.queryStatus(localNo, appChlModel, null);
        return BaseRes.resp(status);
    }

    @ApiOperation("发起退款")
    @PostMapping(value = "orderRefund")
    public BaseRes orderRefund(
            @ApiParam("应用方的交易流水号") @RequestParam @NotNull String localNo,
            @ApiParam("应用方的退款订单号") @RequestParam @NotNull String appRefundNo,
            @ApiParam("应用方退款金额") @RequestParam @NotNull Integer amt
    ) {
        // 找到支付记录
        PayModel payModel = payService.query(localNo);
        if (null == payModel) {
            ErrUtils.appThrow(ApiCode.PAY_TRAN_NOT_FOUND);
        }
        if (payModel.getEntity().getChlType().equals(ChlEnum.IAP)) {
            ErrUtils.appThrow(ApiCode.NOT_SUPPORT_IAP_REFUND);
        }
        // 找到记录所属应用
        AppModel appModel = appService.query(payModel.getEntity().getAppId());
        if (null == appModel) {
            ErrUtils.appThrow(ApiCode.APP_NOT_FOUND);
        }
        // 验证签名
        checkSign(appModel);
        // 找到应用配置
        AppChlModel appChlModel = appChlService.query(payModel.getEntity().getAppId(), payModel.getEntity().getChlType());
        if (null == appChlModel) {
            ErrUtils.appThrow(ApiCode.APP_CHL_NOT_FOUND);
        }
        // handle
        PayStatusEnum status = payService.orderRefund(appRefundNo, localNo, appModel, appChlModel, amt, payModel);
        return BaseRes.resp(status == PayStatusEnum.SUCC ? BaseCode.SUCC : BaseCode.FAILED, "", status);
    }

    /**
     * 验证签名
     *
     * @param appModel
     */
    public void checkSign(AppModel appModel) {
        Map<String, String> params = SpringUtils.getParam(req());
        boolean isvalid = appModel.checkSign(params);
        if (!isvalid) {
            ErrUtils.appThrow(ApiCode.APP_SIGN_MISMATCH);
        }
    }
}
